﻿
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Text;

namespace TcpSocketTaskDemo.Echo
{
    sealed class Client : IDisposable
    {
        // *************************************************************** //
        #region [ member fields ]

        private readonly AwaitSocket m_awaitSocket;
        private readonly IPEndPoint m_svrEndpoint;

        #endregion

        // *************************************************************** //
        #region [ constructor ]

        public Client(IPEndPoint svrEndpnt)
        {
            m_svrEndpoint = svrEndpnt;
            m_awaitSocket = new AwaitSocket(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), 1024);
        }

        #endregion

        // *************************************************************** //
        #region [ destructor ]

        private bool m_disposed;

        ~Client()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (m_disposed)
                return;

            try
            {
                if (disposing)
                {
                    m_awaitSocket.Dispose();
                }
            }
            finally
            {
                m_disposed = true;
            }
        }

        #endregion

        // *************************************************************** //
        #region [ public APIs ]

        public void Start(IEnumerable<string> messages)
        {
            Task connectTask = m_awaitSocket.ContinueConnect(m_svrEndpoint);
            ((Task<object>)connectTask).ContiExcepToDispose(this);

            connectTask.ContinueWith(prevTask =>
                                         {
                                             Console.WriteLine("server connected.");

                                             Receive();

                                             foreach (string msg in messages)
                                             {
                                                 Task<int> sendTask = m_awaitSocket.Send(Encoding.ASCII.GetBytes(msg));
                                                 sendTask.Wait();// blocking send here is not a big problem
                                             }//foreach
                                         }, TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        #endregion

        // *************************************************************** //
        #region [ private helpers ]

        private void Receive()
        {
            Task<Tuple<byte[], int>> recvTask = m_awaitSocket.Receive();
            recvTask.ContiExcepToDispose(this);
            recvTask.ContinueWith(prevTask =>
                                      {
                                          Tuple<byte[], int> recved = prevTask.Result;
                                          string echoed = Encoding.ASCII.GetString(recved.Item1, 0, recved.Item2);
                                          Console.WriteLine(echoed);

                                          Receive();
                                      }, TaskContinuationOptions.OnlyOnRanToCompletion);
        }

        #endregion
    }
}
